package com.atguigu.service.impl;

import com.atguigu.constant.RedisConst;
import com.atguigu.entity.PrepareSeckillOrder;
import com.atguigu.entity.SeckillProduct;
import com.atguigu.entity.UserSeckillSkuInfo;
import com.atguigu.mapper.SeckillProductMapper;
import com.atguigu.result.RetVal;
import com.atguigu.result.RetValCodeEnum;
import com.atguigu.service.SeckillProductService;
import com.atguigu.util.MD5;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hachimen
 * @since 2021-09-13
 */
@Service
public class SeckillProductServiceImpl extends ServiceImpl<SeckillProductMapper, SeckillProduct> implements SeckillProductService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public SeckillProduct getSeckillProductBySkuId(Long skuId) {
        //根据skuId 获取秒杀对象数据
        return (SeckillProduct) redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).get(skuId+"");
    }

    @Override
    public void prepareSeckill(UserSeckillSkuInfo userSeckillSkuInfo) {
        //预下单业务
        Long skuId = userSeckillSkuInfo.getSkuId();
        String userId = userSeckillSkuInfo.getUserId();
        //判断商品是否售完
        String state = (String)redisTemplate.opsForValue().get(RedisConst.SECKILL_STATE_PREFIX + skuId);
        if(RedisConst.CAN_NOT_SECKILL.equals(state)){
            return;//已售完
        }
        //a.把用户抢购的商品放入缓存 用于该用户是否抢购过
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(RedisConst.PREPARE_SECKILL_USERID_SKUID + userId, skuId, RedisConst.PREPARE_SECKILL_LOCK_TIME, TimeUnit.SECONDS);
        if(!aBoolean){
            //已经下过单了
            return;
        }
        //b.从库存中减库存 从list集合中弹出一个
        String stockSkuId = (String)redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).rightPop();
        //c.如果该库存没有了，通知其他节点该商品不能秒杀
        if(StringUtils.isEmpty(stockSkuId)){
            redisTemplate.convertAndSend(RedisConst.PREPARE_PUB_SUB_SECKILL,skuId+":"+RedisConst.CAN_NOT_SECKILL);
            return;
        }
        //d.把用户抢购的预下单商品信息存储到redis
        PrepareSeckillOrder prepareSeckillOrder = new PrepareSeckillOrder();
        prepareSeckillOrder.setBuyNum(1);
        prepareSeckillOrder.setUserId(userId);
        SeckillProduct seckillProduct = getSeckillProductBySkuId(skuId);
        prepareSeckillOrder.setSeckillProduct(seckillProduct);
        prepareSeckillOrder.setPrepareOrderCode(MD5.encrypt(userId+skuId));
        redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).put(userId,prepareSeckillOrder);
        //e.更新秒杀商品库存信息MySQL+Redis
        updateSecKillStock(skuId);
    }

    @Override
    public RetVal hasQualified(String userId, Long skuId) {
        //判断用户是否有抢购资格
        Boolean aBoolean = redisTemplate.hasKey(RedisConst.PREPARE_SECKILL_USERID_SKUID + userId);
        //a.如果预下单中有用户的信息
        if(aBoolean){
            //有预下单信息 获取预下单商品信息
            PrepareSeckillOrder prepareSeckillOrder = (PrepareSeckillOrder) redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).get(userId);
            //当用户预下单成功就具有抢购资格
            if(prepareSeckillOrder != null){
                //有秒杀资格
                return RetVal.build(prepareSeckillOrder, RetValCodeEnum.PREPARE_SECKILL_SUCCESS);
            }
        }
        String orderId = (String) redisTemplate.boundHashOps(RedisConst.BOUGHT_SECKILL_USER_ORDER).get(userId);
        if(orderId != null){
         //已下过单，不存在预下单信息和预下单商品信息，但是存在秒杀订单成功的交易信息
         return RetVal.build(orderId,RetValCodeEnum.SECKILL_ORDER_SUCCESS);
        }
        //排队中
        return RetVal.build(null,RetValCodeEnum.SECKILL_RUN);
    }

    private void updateSecKillStock(Long skuId) {
        //拿取商品剩余库存量
        Long stockCount = redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).size();
        //更新数据库 设置更新频率 自定义一个规则
        if(stockCount%2==0){
            SeckillProduct seckillProduct = getSeckillProductBySkuId(skuId);
            seckillProduct.setStockCount(stockCount.intValue());
            baseMapper.updateById(seckillProduct);
            //更新redis缓存 为了给用户看 有没有机会秒到
            redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).put(skuId,seckillProduct);
        }
    }
}
